﻿using System;
using System.Text;
using System.IO;
using CCL.Utility;

namespace CCL.SocketMessage.Response
{
    /// <summary>
    /// 파일 다운로드 요청에 대한 응답을 제공합니다.
    /// </summary>
    public sealed class FileDownload : ResponseBody
    {
        /// <summary>
        /// 전송하고자 하는 파일이름을 가져옵니다.
        /// </summary>
        public string FileName { get; private set; }       // Constant.Length.FileName (256byte)

        /// <summary>
        /// 전송하고자 하는 파일의 크기를 가져옵니다.
        /// </summary>
        public int FileSize { get; private set; }

        /// <summary>
        /// 전송하려는 파일의 Revision을 가져옵니다.
        /// </summary>
        public int Revision { get; private set; }

        /// <summary>
        /// 전송하고자 하는 파일의 바이트배열을 가져옵니다.
        /// </summary>
        public byte[] FileBytes { get; private set; }

        /// <summary>
        /// 성공한 응답을 생성합니다.
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="revision"></param>
        public FileDownload(string filePath, int revision)
            : base()
        {
            FileInfo fi = new FileInfo(filePath);
            FileName = fi.Name;

            Revision = revision;

            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                FileSize = Convert.ToInt32(fs.Length);
                BinaryReader br = new BinaryReader(fs);
                FileBytes = br.ReadBytes(FileSize);

                br.Dispose();
            }
        }

        /// <summary>
        /// 실패한 응답을 생성합니다.
        /// </summary>
        /// <param name="errorMessage"></param>
        public FileDownload(string errorMessage)
            : base(errorMessage)
        {

        }

        /// <summary>
        /// 직렬화된 byte 배열을 통해 FileDownload 객체를 생성합니다.
        /// </summary>
        /// <param name="bytes">직렬화된 byte 배열</param>
        public FileDownload(byte[] bytes)
            : base(bytes)
        {
            if (Result == Constant.Result.Success)
            {
                int startIndex = 1;

                FileName = Utility.StringUtility.ConvertToStr(Encoding.Default.GetChars(bytes, startIndex, Constant.Length.FileName));
                startIndex += Constant.Length.FileName;

                FileSize = BitConverter.ToInt32(bytes, startIndex);
                startIndex += sizeof(Int32);

                Revision = BitConverter.ToInt32(bytes, startIndex);
                startIndex += sizeof(Int32);

                FileBytes = new byte[FileSize];
                Array.Copy(bytes, startIndex, FileBytes, 0, FileSize);
                startIndex += FileBytes.Length;
            }
        }

        /// <summary>
        /// 바이트 배열로 직렬화 합니다.
        /// </summary>
        /// <returns>직렬화된 바이트 배열</returns>
        public override byte[] GetByte()
        {
            byte[] baseBytes = base.GetByte();
            byte[] temp;
            
            if (Result == Constant.Result.Success)
            {
                int start = 1;

                temp = StringUtility.StringToBytes(FileName, Constant.Length.FileName);
                Array.Copy(temp, 0, baseBytes, start, temp.Length);
                start += temp.Length;

                temp = BitConverter.GetBytes(FileSize);
                Array.Copy(temp, 0, baseBytes, start, temp.Length);
                start += temp.Length;

                temp = BitConverter.GetBytes(Revision);
                Array.Copy(temp, 0, baseBytes, start, temp.Length);
                start += temp.Length;

                temp = FileBytes;
                Array.Copy(temp, 0, baseBytes, start, temp.Length);
            }

            return baseBytes;
        }

        /// <summary>
        /// 직렬화 가능한 객체의 바이트 사이즈를 가져옵니다.
        /// </summary>
        /// <returns>직렬화된 바이트 사이즈</returns>
        public override uint GetSize()
        {
            uint size = base.GetSize();

            // 성공했을 시에는 ErrorMessage를 제외한 나머지 필드의 사이즈를 더한다.
            if (Result == Constant.Result.Success)
            {
                size += Constant.Length.FileName;       // FileName
                size += sizeof(Int32);                  // FileSize
                size += sizeof(Int32);                  // Revision
                size += (uint)FileBytes.Length;         // File Body
            }

            return size;
        }
    }
}
